package com.shiguiwu.springboot3.feign;

import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.TypeUtil;
import cn.hutool.json.JSONUtil;
import feign.FeignException;
import feign.Response;
import feign.Util;
import feign.codec.DecodeException;
import feign.codec.Decoder;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.cloud.openfeign.support.HttpMessageConverterCustomizer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.HttpMessageConverterExtractor;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @description: 自定义
 * @author: stone
 * @date: Created by 2023/9/22 14:16
 * @version: 1.0.0
 * @pakeage: com.shiguiwu.springboot3.feign
 */

public class CustomSpringDecoder implements Decoder {
    private final ObjectFactory<HttpMessageConverters> messageConverters;
    private final ObjectProvider<HttpMessageConverterCustomizer> customizers;



    public CustomSpringDecoder(ObjectFactory<HttpMessageConverters> messageConverters, ObjectProvider<HttpMessageConverterCustomizer> customizers) {
        this.messageConverters = messageConverters;
        this.customizers = customizers;
    }

    @Override
    public Object decode(final Response response, Type type) throws IOException, FeignException {
        if (!(type instanceof Class) && !(type instanceof ParameterizedType) && !(type instanceof WildcardType)) {
            throw new DecodeException(response.status(), "type is not an instance of Class or ParameterizedType: " + type, response.request());
        } else {
            List<HttpMessageConverter<?>> converters = this.messageConverters.getObject().getConverters();
            this.customizers.forEach((customizer) -> {
                customizer.accept(converters);
            });
            HttpMessageConverterExtractor<?> extractor = new HttpMessageConverterExtractor<>(type, converters);
            return extractor.extractData(new FeignResponseAdapter(response, type));
        }
    }

    @Slf4j
    private static final class FeignResponseAdapter implements ClientHttpResponse {
        private final Response response;
        private final Type type;
        private byte[] body;
        private static String aclass = "com.shiguiwu.springboot3.util.R";

        private static String methodName = "getData";

        private FeignResponseAdapter(Response response, Type type) throws IOException {
            this.response = response;
            this.type = type;
            String bodyString = Util.toString(response.body().asReader(StandardCharsets.UTF_8));

            String typeName = type.getTypeName();

            if (typeName.contains(aclass)) {
                this.body = bodyString.getBytes(StandardCharsets.UTF_8);
            }
            else {
                Class<?> aClass = TypeUtil.getClass(type);
                Object bean = null;
                try {
                    bean = JSONUtil.toBean(bodyString, Class.forName(aclass));
                } catch (ClassNotFoundException e) {
                    log.error("找不到类", e);
                    throw new RuntimeException(e);
                }
                Object data = ReflectUtil.invoke(bean, methodName);
                if (ClassUtil.isSimpleValueType(aClass)) {
                    this.body = data.toString().getBytes(StandardCharsets.UTF_8);
                }
                else {
                    this.body = JSONUtil.toJsonStr(data).getBytes(StandardCharsets.UTF_8);
                }
            }

        }

        public HttpStatus getStatusCode() {
            return HttpStatus.valueOf(this.response.status());
        }

        public int getRawStatusCode() {
            return this.response.status();
        }

        public String getStatusText() {
            return this.response.reason();
        }

        public void close() {
            try {
                this.response.body().close();
            } catch (IOException var2) {
            }

        }

        public InputStream getBody() throws IOException {
            return this.body == null ? this.response.body().asInputStream() : new ByteArrayInputStream(this.body);
        }

        public HttpHeaders getHeaders() {
            HttpHeaders httpHeaders = new HttpHeaders();
            Iterator var2 = response.headers().entrySet().iterator();

            while(var2.hasNext()) {
                Map.Entry<String, Collection<String>> entry = (Map.Entry)var2.next();
                httpHeaders.put((String)entry.getKey(), new ArrayList((Collection)entry.getValue()));
            }

            return httpHeaders;
        }
    }
}
